pub mod backward;
pub mod forward;

#[cfg(test)]
mod test {

    #[test]
    fn forward_diff_works() {
        use crate::forward::Variable;

        // define f(x) = sin(exp(x))
        let f = |x: Variable| -> Variable { x.exp().sin() };
        // define variable = 3.0 + epsilon
        let x = Variable::new_diff(3.0);
        // get df/dx (x = 3.0) which very close to 6.6
        let grad = f(x).grad();
        assert!((grad - 6.6).abs() < 1e-5);

        // define f(x) = sqrt(x^2 + y^2) or hypot(x,y)
        let f = |x: Variable, y: Variable| -> Variable { (x.powi(2) + y.powi(2)).sqrt() };
        let x = Variable::new(3.0);
        let y = Variable::new_diff(4.0);

        // get f(x,y) and ∂f/∂y (x = 3.0, y = 4.0)
        let (value, grad) = f(x, y).value_grad();
        assert!((value - 5.0).abs() < 1e-12);
        // in forward mode you can reuse variable to get grad directly
        assert!((grad - y.hypot(x).grad()).abs() < 1e-12);
        // you cannot get all grads at a time
    }

    #[test]
    fn backward_diff_works() {
        use crate::backward::Graph;
        // define calc graph, use unique number to mark the graph
        let mut graph: Graph<0> = Graph::new();
        let x = graph.new_variable(3.0);
        // define f = sin(exp(x))
        let f = x.exp().sin();
        // calculate f(x) and df (x = 3.0)
        graph.backward(f);
        // calculate df/dx (x = 3.0)
        let grad = graph.grad(x);
        assert!((grad - 6.6).abs() < 1e-5);

        let mut graph: Graph<1> = Graph::new();
        let x = graph.new_variable(3.0);
        let y = graph.new_variable(4.0);
        let f = (x.powi(2) + y.powi(2)).sqrt();
        graph.backward(f);
        // in backward mode you can get all grads at a time
        let f_x = graph.grad(x);
        let f_y = graph.grad(y);
        let value = graph.value(f);
        assert!((value - 5.0).abs() < 1e-12);
        assert!((f_x - 0.6).abs() < 1e-12);
        assert!((f_y - 0.8).abs() < 1e-12);
        // you cannot reuse variable
    }
}
